package com.atguigu.gmall.getway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.getway.constant.RedisConst;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntFunction;

/**
 * @Description: gateway网关过滤器 自定义过滤器
 * @Author: Mr.Cao
 * @DateTime: 2021/10/18 19:29
 * BMW F750GS
 **/
@Component
public class MyGatewayFilter implements GlobalFilter, Ordered {
    //路径匹配
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    private RedisTemplate redisTemplate;
    //重定向地址
    public static final String REDIRECT_URL = "http://passport.gmall.com/login.html?originUrl=";
    @Value("gateway.urls")
    private String[] urls;
    //过滤器
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求体对象
        ServerHttpRequest request = exchange.getRequest();
        //获取响应对象
        ServerHttpResponse response = exchange.getResponse();

        //1. aip/user/passport/inner/**
        String path = request.getURI().getPath();
        System.out.println("path = " + path);

        //2: http://cart.gmall.com/path...
        String url = request.getURI().toString();
        System.out.println("url:" + url);

        //1:校验请求路径 内部资源  /inner  不允许  非法访问  没有权限
        if(antPathMatcher.match("/**/inner/**", path)){
            //就是内部资源  不允许访问
            return getVoidMono(response,ResultCodeEnum.PERMISSION);
        }

        //2:校验用户是否登录
        String userId = getUserId(request);
        //3:登录
        //3.1:有些路径 在不登录时也是允许访问 www.gmall.com/  list.gmall.com/list.html
        //3.2:有些路径 在不登录时也是不允许访问   /**/auth/** 此路径必须是登录
        if(antPathMatcher.match("/**/auth/**", path) && StringUtils.isEmpty(userId)){//异步
            //路径要求必须登录 并且 用户还未登录
            return getVoidMono(response,ResultCodeEnum.LOGIN_AUTH);//适用于 异步
        }
        //   https://cart.jd.com/addToCart.html  www.gmall.com/  list.gmall.com/list.html
        for (String u : urls) {//    trade.html,pay.html,order.html  //适合同步
            if(path.contains(u) && StringUtils.isEmpty(userId)){  //  path=/trade.html   url=trade.html
                //路径要求必须登录 并且 用户还未登录
                //重定向 响应码  303   响应头:重定向的路径
                response.setStatusCode(HttpStatus.SEE_OTHER);
                try {
                    //添加响应头
                    response.getHeaders().add(HttpHeaders.LOCATION,
                            REDIRECT_URL + URLEncoder.encode(url,"utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                //响应： 包含 响应行 响应头 响应码  响应体 URLDecoder.decode
                return response.setComplete();//开始响应

            }
        }
        //传递真实的用户Id
        if (!StringUtils.isEmpty(userId)){
            //将用户Id设置到请求头, 错误写法, 爆异常, 请求头不允许更改
            //request.getHeaders().add("userId",userId);
            //将原来的请求对象原封不动的重新构建一个请求对象
            //ServerHttpRequest.Builder mutate = request.mutate();
            //添加请求头的信息
            request.mutate().header("userId",userId);
        }
        String tempUserId =  getTempUserId(request);
        if (!StringUtils.isEmpty(tempUserId)){
            //将用户Id设置到请求头, 错误写法, 爆异常, 请求头不允许更改
            //request.getHeaders().add("userId",userId);
            //设置到请求头,
            request.mutate().header("userTempId",tempUserId);
        }

        //5.放行
        return chain.filter(exchange);
    }
    //获取临时的用户Id
    private String getTempUserId(ServerHttpRequest request) {
        //从请求头中获取临时的用户Id
        String tempUserId = request.getHeaders().getFirst("userTempId");
        if (StringUtils.isEmpty(tempUserId)){
            //如果请求头中没有,获取Cookies中的
            HttpCookie userTempId = request.getCookies().getFirst("userTempId");
            if (userTempId!=null) {
                tempUserId = userTempId.getValue();
            }
            ArrayList<String> list = new ArrayList<>();
        }
        return tempUserId;
    }

    //统一返回值  适用于ajax异步请求
    private Mono<Void> getVoidMono(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        Result<Object> result = Result.build(null, resultCodeEnum);
        String json = JSONObject.toJSONString(result);// "{code:209,"message:没有权限}"
        DataBuffer dataBuffer = response.bufferFactory().wrap(json.getBytes());
        //设置 响应的编码
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
        //content-type: text/html;charset=UTF-8  页面同步
        //content-type: application/json;charset=UTF-8
        return response.writeWith(Mono.just(dataBuffer));
    }
    
    //获取用户ID的:从请求头中或Ccookie中获取
    private String getUserId(ServerHttpRequest request) {
        //登录 种类
        //1 AJax异步  不带Cookie 需要将Cookie中的令牌转移到请求头中   VUE 请求头 JWT
        //2 同步的 window.location.href = url // 带Cookie  Cookie中的令牌

        //1:获取token
        //1.1 从请求头中获取
        String token = request.getHeaders().getFirst("token");
        if(StringUtils.isEmpty(token)){
            //1.2 从Cookie中获取
            HttpCookie httpCookie = request.getCookies().getFirst("token");
            if(null != httpCookie){
                token = httpCookie.getValue();
            }
        }
        if(!StringUtils.isEmpty(token)){
            //2:根据token去缓存中获取UserId
            if(redisTemplate.hasKey(RedisConst.USER_LOGIN_KEY_PREFIX + token)){
                return (String) redisTemplate.opsForValue().
                        get(RedisConst.USER_LOGIN_KEY_PREFIX + token);
            }
        }
        return  null;
    }
    //过滤器的优先级
    @Override
    public int getOrder() {
        return 0;
    }
}
